package com.lsk.mps.backend.aspect;

import com.google.gson.Gson;
import com.lsk.mps.backend.aspect.annotation.Cached;
import com.lsk.mps.backend.redis.RedisDao;
import com.lsk.mps.backend.util.ReflectionUtil;
import com.lsk.mps.backend.util.SpringUtil;
import com.lsk.mps.backend.util.StatusCode;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class CachedAspect {
    @Autowired
    private RedisDao redisDao;
    @Autowired
    private Gson gson;

    private static Map<String, Class<?>> cacheTypes = new HashMap<>();

    @Pointcut("@annotation(com.lsk.mps.backend.aspect.annotation.Cached)")
    public void pointcut() {}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            Cached cached = ReflectionUtil.getAnnotation(pjp, Cached.class);
            String cacheName = cached.value();
            String cacheKey = "CACHE-" + cacheName;
            if (cached.sameArguments()) {
                Object[] args = pjp.getArgs();
                for (Object arg : args) {
                    cacheKey += "-";
                    cacheKey += arg.toString();
                }
            }
            if (redisDao.exists(cacheKey)) {
                String json = redisDao.get(cacheKey);
                Class<?> type = cacheTypes.get(cacheName);
                return gson.fromJson(json, type);
            } else {
                Object result = pjp.proceed();
                cacheTypes.put(cacheName, result.getClass());
                redisDao.set(cacheKey, gson.toJson(result), cached.expire());
                return result;
            }
        } catch (StatusCode statusCode) {
            throw statusCode;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    public static void expireCache(String cacheName) {
        cacheTypes.remove(cacheName);
        RedisDao redis = SpringUtil.getBean(RedisDao.class);
        redis.delete("CACHE-" + cacheName);
    }
}
